#include "nnslite.h"
#include "nnslitememory.h"

static NNSLITE_RET CheckStoreParameters(nnsliteParameters* params);

/** nnsliteStore  */
NNSLITE_RET nnsliteStoreCreate(nnsliteStore* store, nnsliteParameters* params)
{
	NNSLITE_RET ret;

	/* Check the parameters from the user */
	ret = CheckStoreParameters(params);
	NNSLITE_ON_FAILED_RETURN(ret);
	/* Copy the name and file name */
	strcpy(store->name, params->name);
	strcpy(store->filename, params->filename);
	store->distfunc = params->distFunc;
	/* Create the heap file system */
	ret = nnsliteHeapFSOpenNew(&store->heapFS, store->filename, params->heapFSBufSize, params->heapFSPageSize, 
			params->heapFSNumPages,params->heapFSFileCount);
	NNSLITE_ON_FAILED_RETURN(ret);
	/* Create the meta file */
	ret = nnsliteBufferedHeapFileOpenNew(store->heapFS, &store->metaFile, params->heapFSBufSize, 0);
	NNSLITE_ON_FAILED_RETURN(ret);
	/* Close the meta file */
	ret = nnsliteBufferedHeapFileClose(&store->metaFile);
	NNSLITE_ON_FAILED_RETURN(ret);
	/* Create the object container base */
	ret = nnsliteObjectContainerBaseCreate(&store->objContainerBase,store->heapFS, params->containerMaxCount, params->heapFSBufSize);
	NNSLITE_ON_FAILED_RETURN(ret);
	/* Create the M-TREE */
	ret = nnsliteMTreeCreate(&store->mtree, params->mtreeMaxNumNodes, params->mtreeMaxDepth, params->mtreeChildCount, 
			store->objContainerBase, params->distFunc);
	NNSLITE_ON_FAILED_RETURN(ret);

	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteStoreClose(nnsliteStore* store)
{
	NNSLITE_RET ret;

	/* Save the store first */
	ret = nnsliteStoreSave(store);
	NNSLITE_ON_FAILED_RETURN(ret);

	/* Free the M-tree */
	ret = nnsliteMTreeFree(store->mtree);
	NNSLITE_ON_FAILED_RETURN(ret);
	store->mtree = NULL;
	/* Free the object containers */
	ret = nnsliteObjectContainerBaseFree(store->objContainerBase);
	NNSLITE_ON_FAILED_RETURN(ret);
	store->objContainerBase = NULL;
	/* Close the meta file */
	ret = nnsliteBufferedHeapFileClose(&store->metaFile);
	NNSLITE_ON_FAILED_RETURN(ret);
	/* Close the heap file system */
	ret = nnsliteHeapFSClose(store->heapFS);
	NNSLITE_ON_FAILED_RETURN(ret);

	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteStoreOpen(nnsliteStore* store, const char* filename, nnsliteDistFunc distfunc)
{
	NNSLITE_RET ret;

	assert(distfunc != NULL);
	
	store->distfunc = distfunc;
	/* Open the heap file system */
	ret = nnsliteHeapFSOpen(&store->heapFS, filename);
	NNSLITE_ON_FAILED_RETURN(ret);

	/* Open the meta heap file */
	ret = nnsliteBufferedHeapFileOpen(store->heapFS,0, &store->metaFile, store->heapFS->defaultBufSize);
	NNSLITE_ON_FAILED_RETURN(ret);

	/* Set the buffered heap file begin to read */
	ret = nnsliteBufferedHeapFileBeginRead(&store->metaFile);
	NNSLITE_ON_FAILED_RETURN(ret);
	/* Read the store's name */
	ret = nnsliteBufferedHeapFileRead(&store->metaFile, store->name, NNSLITE_MAX_NAMELEN);
	NNSLITE_ON_FAILED_RETURN(ret);

	/* Read the object containers */
	ret = nnsliteObjectContainerBaseRead(&store->objContainerBase,store->heapFS, &store->metaFile);
	NNSLITE_ON_FAILED_RETURN(ret);

	/* Read the M-tree */
	ret = nnsliteMTreeRead(&store->mtree, &store->metaFile);
	NNSLITE_ON_FAILED_RETURN(ret);

	/* Close the meta file */
	ret = nnsliteBufferedHeapFileClose(&store->metaFile);
	NNSLITE_ON_FAILED_RETURN(ret);

	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteStoreSave(nnsliteStore* store)
{
	NNSLITE_RET ret;
	nnsliteBufferedHeapFile* metaFile = &store->metaFile;
	/* Open the meta heap file */
	ret = nnsliteBufferedHeapFileOpenNew(store->heapFS,&store->metaFile, store->heapFS->defaultBufSize, 0);
	NNSLITE_ON_FAILED_RETURN(ret);
	/* Write the store's name */
	nnsliteBufferedHeapFileWrite(metaFile, store->name, NNSLITE_MAX_NAMELEN);
	/* Write the object container's base */
	nnsliteObjectContainerBaseWrite(store->objContainerBase, metaFile);
	/* Write the M-tree */
	ret = nnsliteMTreeWrite(store->mtree, metaFile);
	NNSLITE_ON_FAILED_RETURN(ret);
	/* Close the meta file */
	ret = nnsliteBufferedHeapFileClose(&store->metaFile);
	NNSLITE_ON_FAILED_RETURN(ret);

	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteStoreNNSearch(nnsliteStore* store, const nnsliteObject* object, u32 top, nnsliteObject* results)
{
	return nnsliteMTreeNNSearch(store->mtree, object, top, results);
}

NNSLITE_RET nnsliteStorePrint(nnsliteStore* store, FILE* fp)
{
	return nnsliteMTreePrint(store->mtree, fp);
}

NNSLITE_RET nnsliteStoreInsert(nnsliteStore* store, nnsliteObject* object)
{
	return nnsliteMTreeInsert(store->mtree, object);
}

static NNSLITE_RET CheckStoreParameters(nnsliteParameters* params)
{
	if (params->distFunc == NULL)
	{
		assert(false);
		return NNSLITE_RET_ERR_IMCOMPLETED_PARAMETER;
	}
	if (params->containerMaxCount == 0)
	{
		assert(false);
		return NNSLITE_RET_ERR_IMCOMPLETED_PARAMETER;
	}
	if (params->heapFSBufSize == 0)
	{
		assert(false);
		return NNSLITE_RET_ERR_IMCOMPLETED_PARAMETER;
	}
	if (params->heapFSFileCount == 0)
	{
		assert(false);
		return NNSLITE_RET_ERR_IMCOMPLETED_PARAMETER;
	}
	if (params->heapFSNumPages == 0)
	{
		assert(false);
		return NNSLITE_RET_ERR_IMCOMPLETED_PARAMETER;
	}
	if (params->heapFSPageSize == 0)
	{
		assert(false);
		return NNSLITE_RET_ERR_IMCOMPLETED_PARAMETER;
	}
	if (params->mtreeChildCount == 0)
	{
		assert(false);
		return NNSLITE_RET_ERR_IMCOMPLETED_PARAMETER;
	}
	if (params->mtreeMaxDepth == 0)
	{
		assert(false);
		return NNSLITE_RET_ERR_IMCOMPLETED_PARAMETER;
	}
	if (params->mtreeMaxNumNodes == 0)
	{
		assert(false);
		return NNSLITE_RET_ERR_IMCOMPLETED_PARAMETER;
	}
	return NNSLITE_RET_OK;
}

